package lang

import (
	"fmt"
)

type WalkFn func(n Node)

func (t *Tree) Walk(fn WalkFn) {
	walkGroup(t.Root, fn)
}

func walkGroup(grp *GroupNode, fn WalkFn) {
	for _, v := range grp.Nodes {
		switch v.Type() {
		case NodeGroup:
			fn(v.(*GroupNode))
			walkGroup(v.(*GroupNode), fn)
			//this is a dirty, dirty hack
			fn(nil)
		default:
			fn(v)
		}
		//fmt.Printf("At %d\n", p.level)
	}
}

const print_spacer = ". "

type printer struct {
	level int
}

var p printer

func prettyPrint(msg string) {
	for i := 0; i < p.level; i++ {
		fmt.Print(print_spacer)
	}
	fmt.Print(msg)
}

func prettyPrintln(msg string) {
	prettyPrint(msg + "\n")
}

func pretty(n Node) {
	if n == nil {
		p.level--
		return
	}
	switch n.Type() {
	case NodeGroup:
		prettyPrintln("NODEGROUP")
		p.level++
	case NodeVarDef:
		prettyPrint("VARDEF(")
		fmt.Print(n.(*VarDefNode).Ident)
		fmt.Println(")")
		p.level++
		pretty(n.(*VarDefNode).Val)
		p.level--
	case NodeIdent:
		prettyPrint("IDENT: ")
		fmt.Println(n.(*IdentNode).Ident)
	case NodeBool:
		prettyPrint("BOOL: ")
		fmt.Println(n.(*BoolNode).Val)
	case NodeNumber:
		prettyPrint("NUMBER: ")
		fmt.Println(n.(*NumberNode).Text)
	case NodeStr:
		prettyPrint("STR: ")
		fmt.Printf("%q\n", n.(*StrNode).Val)
	case NodeRStr:
		prettyPrint("RSTR: ")
		fmt.Printf("'%s'\n", n.(*RStrNode).Val)
	case NodeArgList:
		walkGroup(n.(*ArgListNode).Args, pretty)
		/*prettyPrintln("ARGLIST")
		p.level++
		/*for _, k := range n.(*ArgListNode).Args {
			pretty(k)
		}*/
		//p.level--
	case NodeDefun:
		//fmt.Printf("DBG: %d\n", p.level)
		prettyPrint("DEFUN: ")
		fmt.Println(n.(*DefunNode).Name)
		p.level++ //XXX why
		//pretty(n.(*DefunNode).Args)
		pretty(n.(*DefunNode).Args)
		walkGroup(n.(*DefunNode).Args, pretty)
		pretty(nil)
		/*p.level += 2
		for _, l := range n.(*DefunNode).Args.Nodes {
			pretty(l)
		}*/
		//p.level--
		pretty(n.(*DefunNode).Body)
		walkGroup(n.(*DefunNode).Body, pretty)
		pretty(nil)
		//XXX whyp.level--
		p.level--
	case NodeReturn:
		prettyPrintln("RETURN")
		//fmt.Printf("Return starts at %d\n", p.level)
		p.level++
		gn, ok := n.(*ReturnNode).Val.(*GroupNode)
		if ok {
			pretty(gn)
			walkGroup(gn, pretty)
			pretty(nil)
		} else if n.(*ReturnNode).Val != nil {

			pretty(n.(*ReturnNode).Val)
		}
		p.level--
		//fmt.Printf("Return ends at %d\n", p.level)
	case NodeBinOp:
		prettyPrint("BINOP: ")
		fmt.Println(n.(*BinOpNode).Op)
		//fmt.Printf("BinOp starts at %d\n", p.level)
		p.level++
		lhg, lok := n.(*BinOpNode).LHS.(*GroupNode)
		if lok {
			pretty(lhg)
			walkGroup(lhg, pretty)
			pretty(nil)
		} else {
			pretty(n.(*BinOpNode).LHS)
		}
		rhg, rok := n.(*BinOpNode).RHS.(*GroupNode)
		if rok {
			pretty(rhg)
			walkGroup(rhg, pretty)
			pretty(nil)
		} else {
			pretty(n.(*BinOpNode).RHS)
		}
		//WTF XXX p.level--
		p.level--
		//fmt.Printf("BinOp ends at %d\n", p.level)
	case NodeCall:
		prettyPrint("CALL(")
		fmt.Print(n.(*CallNode).Target)
		fmt.Println(")")
		p.level++
		//pretty(n.(*CallNode).Args)
		pretty(n.(*CallNode).Args.Args)
		//fmt.Printf("Call starts at %d\n", p.level)
		walkGroup(n.(*CallNode).Args.Args, pretty)
		//fmt.Printf("Call ends at %d\n", p.level)
		pretty(nil)
		/*for _, l := range n.(*CallNode).Args.Nodes {
			pretty(l)
		}
		p.level -= 2*/
		p.level--
	case NodeBranch:
		prettyPrintln("BRANCH")
		p.level++
		gr, ok := n.(*BranchNode).Cond.(*GroupNode)
		if ok {
			pretty(gr)
			walkGroup(gr, pretty)
			pretty(nil)
		} else {
			if n.(*BranchNode).Cond == nil {
				prettyPrintln("NIL")
			} else {
				pretty(n.(*BranchNode).Cond)
			}
		}
		if n.(*BranchNode).True == nil {
			prettyPrintln("NIL")
		} else {
			pretty(n.(*BranchNode).True)
			walkGroup(n.(*BranchNode).True, pretty)
			pretty(nil)
		}
		if n.(*BranchNode).False == nil {
			prettyPrintln("NIL")
			//FIXME (TODO) we could handle this properly
		} else {
			pretty(n.(*BranchNode).False)
			walkGroup(n.(*BranchNode).False, pretty)
			pretty(nil)
		}
		p.level--
	case NodeLoop:
		prettyPrintln("LOOP")
		p.level++
		gr, ok := n.(*LoopNode).Pre.(*GroupNode)
		if ok {
			pretty(gr)
			walkGroup(gr, pretty)
			pretty(nil)
		} else {
			if n.(*LoopNode).Pre == nil {
				prettyPrintln("NIL")
			} else {
				pretty(n.(*LoopNode).Pre)
			}
		}
		gr2, ok2 := n.(*LoopNode).Cond.(*GroupNode)
		if ok2 {
			pretty(gr2)
			walkGroup(gr2, pretty)
			pretty(nil)
		} else {
			if n.(*LoopNode).Cond == nil {
				prettyPrintln("NIL")
			} else {
				pretty(n.(*LoopNode).Cond)
			}
		}

		gr3, ok3 := n.(*LoopNode).Post.(*GroupNode)
		if ok3 {
			pretty(gr3)
			walkGroup(gr3, pretty)
			pretty(nil)
		} else {
			if n.(*LoopNode).Post == nil {
				prettyPrintln("NIL")
			} else {
				pretty(n.(*LoopNode).Post)
			}
		}
		pretty(n.(*LoopNode).Body)
		walkGroup(n.(*LoopNode).Body, pretty)
		pretty(nil)
		p.level--

	}
}

func (t *Tree) PrettyPrint() {
	p = printer{level: 0}
	t.Walk(pretty)
}
